<?php
/**
 * 文件描述  模板解析类
 * =================================================================
 * 文件名称  Template.class.php
 * -----------------------------------------------------------------
 * 作    者  xiaokai<myxiaokai@gmail.com>
 * -----------------------------------------------------------------
 * 创建时间  2010-1-20
 * -----------------------------------------------------------------
 * 版权信息  创思工作室
 * =================================================================
 */

class Template
{
    public $_tplPath      = '';
    public $_cahcePath    = '';
    public $_tplSuffix    = '.html';
    protected $_vars      = array();
    protected $_tplFile   = '';
    protected $_cacheFile = '';

    /**
     * 设置模板变量
     * @access  public
     * @param   string  $name   变量名
     * @param   mixed   $value  对应的值
     * @return  bool
     */
    public function assign($name, $value = '')
    {
        if (is_array($name)) {
            foreach ($name as $key => $val) {
                $this->_vars[$key] = $val;
            }
        } else {
            $this->_vars[$name] = $value;
        }
        return true;
    }

    /**
     * 魔术方法设置模板变量
     * @access  public
     * @param   string  $name   变量名
     * @param   mixed   $value  对应的值
     * @return  bool
     */
    public function __set($name, $value)
    {
        $this->assign($name, $value);
    }

    public function display($tpl, $vars = array())
    {
        $this->fetch($tpl, $vars, true);
    }

    public function fetch($tpl, $vars = array(), $display = false)
    {
        if (!empty($vars)) {
            $this->assign($vars);
        }
        $this->initPath($tpl);
        if ($this->isCache()) {
            $this->cache();
        }
        ob_start();
        extract($this->_vars);
        include $this->_cacheFile;
        if (!$display === true) {
            $content = ob_get_contents();
            ob_end_clean();
            return $content;
        }
    }

    /**
     * 缓存解析后的模板
     * @access  public
     * @return  bool
     */
    protected function cache()
    {
        $content = file_get_contents($this->_tplFile);
        $content = preg_replace('/(\{)(\S.+?)(\})/ies', "\$this->compile('\\2')", $content);
        return  file_put_contents($this->_cacheFile, $content);
    }

    /**
     * 编译模板
     *
     * @access  public
     * @param   string  模板定界符内的字符串
     * @return  string  编译后的字符串
     */
    protected function compile($str)
    {
        static $_parses = array();
        $str  = trim(stripslashes($str));

        //还原非模板标签 例如{124}
        if (preg_match('/^[\s|\d]/is', $str)) {
            return '{' . $var . '}';
        }

        if (isset($_parses[$str])) {
            return $_parses[$str];
        }

        $flag = substr($str, 0, 1);
        $var  = substr($str, 1);

        //{$a} {$a|time="Y-m-d H:i:s"}  一般变量输出
        if ($flag == '$') {
            $arr = explode('|', $var);
            $var = array_shift($arr);
            $code = "\${$var}"; //$var变量名称, 比如 a  那么code就是$a
            $len = count($arr); //统计函数个数
            for ($i = 0; $i < $len; $i++) {
                $args    = explode('=', $arr[$i]);  //date="Y-m-d",### 将函数名和参数分割出来
                $args[0] = trim($args[0]);  //清空函数名旁的空白
                if (isset($args[1])) {  //如果存在参数
                    if (strstr($args[1], '###')) {
                        $args[1] = str_replace('###', $code, $args[1]);  //将### 换为输出变量自己
                        $code = "{$args[0]}({$args[1]})";
                    } else {
                        $code = "{$args[0]}({$code},{$args[1]})";
                    }
                } else {//不存在参数, 那么自己是参数
                    $code = "{$args[0]}({$code})";
                }
            }
            $_parses[$str] = "<?php echo ({$code}); ?>";
        } else if ($flag == '#') {  //执行函数 {~include('file')}
            $_parses[$str] = "<?php {$var}  ?>";
        } else if ($flag == '@') { // 输出函数运行的结果
            $_parses[$str] = "<?php echo ({$var}) ?>";
        } else if ($flag == '~') {  //输出常量
            $_parses[$str] = '<?php echo (' . strtoupper($var) . '); ?>';
        } else if ($flag == '/') {  // {/*} 闭合标签
            switch (strtolower($var)) {
                case 'loop' : $end = '$loopIndex++; $loopKey++; endforeach; endif;'; break;
                case 'if'   : $end = 'endif;'; break;
                case 'for'  : $end = 'endfor;'; break;
            }
            $_parses[$str] = "<?php {$end} ?>";
        }

        // include 标签
        if (substr($str, 0, 7) == 'include') {
            $file = trim(substr($str, 7));
            $file = strtr($file, '.', '/');
            $_parses[$str] = "<?php \$this->display('{$file}'); ?>";
        }

        //foreach循环
        if (substr($str, 0, 4) == 'loop') {
            $args    = explode(' ', substr($var, 4));
            $foreach = '<?php if (is_array('.$args[0].') || is_object('.$args[0].')) : $loopIndex = 1; $loopKey = 0; foreach(' . $args[0] . ' as ' . $args['1'];
            if (count($args) == 3) {
                $foreach .= ' => ' . $args[2];
            }
            $_parses[$str] =  $foreach . ') : ?>';
        }

        //if标签
        if (substr($str, 0, 2) == 'if') {
            $exp           = substr($var, 2);
            $_parses[$str] ="<?php if ({$exp}) : ?>";
        }

        //elseif
        if (substr($str, 0, 6) == 'elseif') {
            $exp           = substr($var, 6);
            $_parses[$str] = "<?php elseif ({$exp}) : ?>";
        }

        //else
        if (substr($str, 0, 4) == 'else') {
            $_parses[$str] = "<?php else : ?>";
        }

        //还原标签
        return isset($_parses[$str]) ? $_parses[$str] : '{' . $flag . $var . '}';
    }
    /**
     * 是否需要更新缓存
     * @access  public
     * @return  bool
     */
    protected function isCache()
    {
        if (filemtime($this->_tplFile) > @filemtime($this->_cacheFile)) {
            return true;
        }
        return false;
    }

    /**
     * 初始化模板文件与缓存文件路径
     * @access  public
     * @param   string  $tpl    模板文件名称
     * @return  bool
     */
    protected function initPath($tpl)
    {
        $this->_tplFile   = $this->_tplPath . $tpl . $this->_tplSuffix;
        $this->_cacheFile = $this->_cahcePath . $tpl . '.php';
        if (!is_file($this->_tplFile)) {
            exit('模板文件不存在');
        }
        return true;
    }
}

$tpl             = new Template();
$tpl->_tplPath   = './Template/';
$tpl->_cahcePath = './Template/';

$tpl->assign('name', 'xiaokai');    //assign赋值
$tpl->assign('header', range('a', 'z'));


$tpl->age = 18;                     //魔术方法复制

$tpl->time = time();
$tpl->str  = "ajsdfklasdjflkajdfkldjklfjd";
$tpl->display('index');       //输出时赋值